﻿using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using Main;

[CanEditMultipleObjects]
[CustomEditor(typeof(LuaInjectionData), true)]
public class LuaInjectionDataInspector : Editor
{
	protected LuaInjectionData m_LuaInjectionData;

	public void OnEnable()
	{
		m_LuaInjectionData = target as LuaInjectionData;
	}

	public override void OnInspectorGUI()
	{
		EditorGUIUtility.labelWidth = 80;

		GUI.changed = false;

		FieldInfo field = typeof(LuaInjectionData).GetField("m_InjectionList", BindingFlags.Instance | BindingFlags.NonPublic);
		List<Injection4> injections = field.GetValue(m_LuaInjectionData) as List<Injection4>;

		//DrawArray(injections, true);

		GUILayout.BeginHorizontal();
		Undo.RecordObject(m_LuaInjectionData, "Injection.Count");
		if (GUILayout.Button("+"))
		{
			injections.Add(new Injection4());
		}
		GUILayout.EndHorizontal();

		field.SetValue(m_LuaInjectionData, injections);

		if (GUI.changed)
		{
			EditorUtility.SetDirty(m_LuaInjectionData);
		}
	}

	private List<T> DrawArray<T>(List<T> array, bool isDict) where T : Injection, new()
	{
		for (int index = 0; index < array.Count; index++)
		{
			GUILayout.BeginHorizontal();

			Undo.RecordObject(m_LuaInjectionData, "Injection.Count");
			bool remove = GUILayout.Button("×", GUILayout.Width(20F), GUILayout.Height(14F));

			T injection = array[index] ?? new T();

			GUILayout.Label("Type:", GUILayout.Width(40F));
			Undo.RecordObject(m_LuaInjectionData, "Injection.Type");
			injection.Type = (InjectionType) EditorGUILayout.EnumPopup(injection.Type, GUILayout.Width(75F));
			if (injection.Type == InjectionType.List || injection.Type == InjectionType.Dict)
			{
				if (typeof(T) == typeof(Injection))
				{
					injection.Type = InjectionType.String;
				}
			}

			GUI.enabled = index > 0;
			Undo.RecordObject(m_LuaInjectionData, "Injection.Up");
			bool up = GUILayout.Button("▲", GUILayout.Width(20F), GUILayout.Height(14F));
			GUI.enabled = index < array.Count - 1;
			Undo.RecordObject(m_LuaInjectionData, "Injection.Down");
			bool down = GUILayout.Button("▼", GUILayout.Width(20F), GUILayout.Height(14F));
			GUI.enabled = true;

			if (isDict)
			{
				GUILayout.Label("Name:", GUILayout.Width(40F));
				injection.Name = HotKeyTextField(injection.Name ?? "", GUILayout.MaxWidth(120F));
			}
			else
			{
				GUILayout.Label("Index:", GUILayout.Width(40F));
				GUI.enabled = false;
				EditorGUILayout.IntField(index + 1, GUILayout.Width(30F));
				GUI.enabled = true;
			}

			object value = injection.Value;
			object newValue = DrawValue<T>(injection.Type, value);
			if (newValue != value)
			{
				injection.Value = newValue;
			}

			GUILayout.EndHorizontal();

			if (remove)
			{
				array.RemoveAt(index);
				index--;
			}
			else if (up)
			{
				array[index] = array[index - 1];
				array[index - 1] = injection;
			}
			else if (down)
			{
				array[index] = array[index + 1];
				array[index + 1] = injection;
			}
			else
			{
				array[index] = injection;
			}
		}
		return array;
	}

	private object DrawValue<T>(InjectionType type, object value) where T : Injection, new()
	{
		switch (type)
		{
			case InjectionType.String:
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = HotKeyTextArea(JsonParser.ObjectToString(value, ""));
				break;
			case InjectionType.Int:
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.IntField(JsonParser.ObjectToInt(value));
				break;
			case InjectionType.Float:
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.FloatField(JsonParser.ObjectToFloat(value));
				break;
			case InjectionType.Boolean:
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.Toggle(JsonParser.ObjectToBool(value));
				break;
			case InjectionType.Object:
				Object obj = value as Object;
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.ObjectField(obj, typeof(Object), true);
				break;
			case InjectionType.GameObject:
				GameObject go = value as GameObject;
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.ObjectField(go, typeof(GameObject), true);
				break;
			case InjectionType.Transform:
				Transform trans = value as Transform;
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.ObjectField(trans, typeof(Transform), true);
				break;
			case InjectionType.Collider:
				Collider collider = value as Collider;
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.ObjectField(collider, typeof(Collider), true);
				break;
			case InjectionType.Behaviour:
				MonoBehaviour behaviour = value as MonoBehaviour;

				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				MonoBehaviour currentBehaviour = EditorGUILayout.ObjectField(behaviour, typeof(MonoBehaviour), true) as MonoBehaviour;
				if (currentBehaviour)
				{
					MonoBehaviour[] behaviours = currentBehaviour.GetComponents<MonoBehaviour>();
					int currentIndex = behaviours.IndexOf(currentBehaviour);
					int behaviourCount = behaviours.Length;
					string[] behaviourNames = new string[behaviourCount];
					int[] behaviourIndexs = new int[behaviourCount];
					for (int index = 0; index < behaviourCount; index++)
					{
						behaviourNames[index] = behaviours[index].GetType().Name;
						behaviourIndexs[index] = index;
					}

					Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
					int behaviourIndex = EditorGUILayout.IntPopup(currentIndex, behaviourNames, behaviourIndexs);
					if (behaviourIndex != currentIndex)
					{
						currentBehaviour = behaviours[behaviourIndex];
					}
				}
				value = currentBehaviour;
				break;
			case InjectionType.Renderer:
				Renderer renderer = value as Renderer;
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.ObjectField(renderer, typeof(Renderer), true);
				break;
			case InjectionType.Curve:
				AnimationCurve curve = value as AnimationCurve;
				GUILayout.Label("Value:", GUILayout.Width(40F));
				Undo.RecordObject(m_LuaInjectionData, "Injection.Value");
				value = EditorGUILayout.CurveField(curve ?? new AnimationCurve());
				break;
			case InjectionType.List:
			case InjectionType.Dict:
				bool isDict = type == InjectionType.Dict;
				if (typeof(T) == typeof(Injection4))
				{
					if (!(value is List<Injection3>))
					{
						value = new List<Injection3>();
					}
					DrawArrayValue(value as List<Injection3>, isDict);
				}
				else if (typeof(T) == typeof(Injection3))
				{
					if (!(value is List<Injection2>))
					{
						value = new List<Injection2>();
					}
					DrawArrayValue(value as List<Injection2>, isDict);
				}
				else if (typeof(T) == typeof(Injection2))
				{
					if (!(value is List<Injection2>))
					{
						value = new List<Injection2>();
					}
					DrawArrayValue(value as List<Injection1>, isDict);
				}
				else if (typeof(T) == typeof(Injection1))
				{
					if (!(value is List<Injection2>))
					{
						value = new List<Injection2>();
					}
					DrawArrayValue(value as List<Injection>, isDict);
				}
				break;
		}
		return value;
	}

	private void DrawArrayValue<T>(List<T> array, bool isDict) where T : Injection, new()
	{
		GUILayout.Label("Size:", GUILayout.Width(40F));
		GUI.enabled = false;
		EditorGUILayout.IntField(array.Count);
		GUI.enabled = true;
		Undo.RecordObject(m_LuaInjectionData, "Injection.Append");
		if (GUILayout.Button("+", GUILayout.Width(20F), GUILayout.Height(14F)))
		{
			array.Add(new T());
		}
		GUILayout.EndHorizontal();
		GUILayout.BeginHorizontal();
		GUILayout.Space(20F);
		GUILayout.BeginVertical();
		DrawArray(array, isDict);
		GUILayout.EndVertical();
	}

	public static string HotKeyTextField(string value, params GUILayoutOption[] options)
	{
		int textFieldID = GUIUtility.GetControlID("TextField".GetHashCode(), FocusType.Keyboard) + 1;
		if (textFieldID == 0)
		{
			return value;
		}
		string newValue = HandleHotKey(textFieldID) ?? value;
		if (newValue != value)
		{
			GUI.changed = true;
		}
		return GUILayout.TextField(newValue, options);
	}

	public static string HotKeyTextArea(string value, params GUILayoutOption[] options)
	{
		int textAreaID = GUIUtility.GetControlID("TextArea".GetHashCode(), FocusType.Keyboard) + 1;
		if (textAreaID == 0)
		{
			return value;
		}
		string newValue = HandleHotKey(textAreaID) ?? value;
		if (newValue != value)
		{
			GUI.changed = true;
		}
		return GUILayout.TextArea(newValue, options);
	}

	public static string HandleHotKey(int controlID)
	{
		if (controlID == GUIUtility.keyboardControl)
		{
			if (Event.current.type == EventType.KeyUp &&
				(Event.current.modifiers == EventModifiers.Control || Event.current.modifiers == EventModifiers.Command))
			{
				if (Event.current.keyCode == KeyCode.X)
				{
					Event.current.Use();
					TextEditor editor = (TextEditor) GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
					editor.Copy();
					editor.Delete();
					return editor.text;
				}
				else if (Event.current.keyCode == KeyCode.C)
				{
					Event.current.Use();
					TextEditor editor = (TextEditor) GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
					editor.Copy();
				}
				else if (Event.current.keyCode == KeyCode.V)
				{
					Event.current.Use();
					TextEditor editor = (TextEditor) GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
					editor.Paste();
#if UNITY_5_3_OR_NEWER
					return editor.text;
#else
                    return editor.content.text;
#endif
				}
				else if (Event.current.keyCode == KeyCode.A)
				{
					Event.current.Use();
					TextEditor editor = (TextEditor) GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
					editor.SelectAll();
				}
			}
		}
		return null;
	}
}
